﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using Mogre;
using MogreNewt;


namespace WastedMechanics
{    
    class HumanController
    {
        public enum HumanControllerState
        {
            Moving,
            Talking,
            Plundering,
            Equipping,
            Bartering,
            GameEnd
        }

        public enum HumanTalkState
        {
            ChoosingReply,
            Replying,
            Pause,
            Listening
        }

        public Character Character;
        public SelectableObject FocusObject;        
        public  IContainer ExchangeObject;
        public bool Run;
        public bool Aim;
        bool LockTarget;
      
        HumanControllerState State;
        HumanTalkState TalkState;
        
        float TalkDelayTime = 0.0f;
        int TextIndex;
        TalkReply CurrentReply;
        TalkReaction CurrentNode;
        SoundInstance CurrentVoice;              

        public HumanController()
        {                                                
        }
            
        public void AttachCharacter(Character character)
        {
            if (Character != character)
            {
                if (Character != null)
                {
                    Character.OnBeginConversation -= BeginConversation;
                    Character.OnEndConversation -= EndConversation;
                    Character.OnBeginEquip -= BeginEquip;
                    Character.OnEndEquip -= EndEquip;
                    Character.OnBeginBarter -= BeginBarter;
                    Character.OnEndBarter -= EndBarter;
                    Character.OnBeginPlunder -= BeginPlunder;
                    Character.OnEndPlunder -= EndPlunder;
                    Character.InterpolateTurning = true;
                }
                if (character != null)
                {
                    character.OnBeginConversation += BeginConversation;
                    character.OnEndConversation += EndConversation;
                    character.OnBeginEquip += BeginEquip;
                    character.OnEndEquip += EndEquip;
                    character.OnBeginBarter += BeginBarter;
                    character.OnEndBarter += EndBarter;
                    character.OnBeginPlunder += BeginPlunder;
                    character.OnEndPlunder += EndPlunder;
                    character.InterpolateTurning = false;

                    Engine.HUDController.LifeBarsFrame.Control.IsVisible = true;
                    Engine.GameCamera.Character = character;
                }
                else
                    Engine.HUDController.LifeBarsFrame.Control.IsVisible = false;

                Character = character;
                
            }
        }        
           
        public void EndTheGame()
        {
            State = HumanControllerState.GameEnd;
            Engine.SoundManager.SoundEffects["explosion.wav"].PlayNewInstance();
            Engine.SoundManager.Musics["nofight.ogg"].Pause();
            Engine.SoundManager.Musics["fight.ogg"].Play();
            Engine.SoundManager.Musics["fight.ogg"].FmodChannel.setVolume(0.6f);
        }

        public void UnloadCurrentVoice()
        {
            if (CurrentVoice != null)
            {
                CurrentVoice.Stop();
                CurrentVoice.SoundSource.Unload();
                CurrentVoice = null;
            }
        }

        public void BeginConversation(SelectableObject character, SelectableObject interlocutor)
        {
            State = HumanControllerState.Talking;
            // Special case - Conversation is triggered by npc
            if (character == Character && CurrentNode == null)
            {                
                Character.State.HaltOrder();
                CurrentNode = interlocutor.TalkResponse.PickReaction();
            }

            Engine.GuiManager.Pointer.IsVisible = true;                        
            Engine.GameCamera.EnterConversationMode(interlocutor);
            Engine.HUDController.Crosshair.IsVisible = false;
            Engine.HUDController.FocusLabel.IsVisible = false;
           
            TalkListen();       
        }

        public void EndConversation(SelectableObject character, SelectableObject interlocutor)
        {
            UnloadCurrentVoice();
            CurrentNode = null;
            Engine.GameCamera.ExitConversationMode();
            State = HumanControllerState.Moving;            
            Engine.GuiManager.Pointer.Hide();
            Engine.HUDController.RepliesFrame.Control.Hide();
            Engine.HUDController.SubtitlesFrame.Control.Hide();
            Engine.HUDController.Crosshair.IsVisible = true;
            Engine.HUDController.FocusLabel.IsVisible = true;
        }

        public void BeginEquip(Character character)
        {
            State = HumanControllerState.Equipping;
            Engine.HUDController.InventoryFrame.Control.Show();
            Engine.GuiManager.Pointer.Show();
            Engine.HUDController.FillInventoryList();
        }

        public void EndEquip(Character character)
        {            
            State = HumanControllerState.Moving;
            Engine.GuiManager.LeaveDragControl();
            Engine.GuiManager.Pointer.Hide();
            Engine.HUDController.InventoryFrame.Control.Hide();
            Engine.HUDController.ClearInventoryList();
            Engine.HUDController.ClearItemDescription(Engine.HUDController.InventoryDescription, Engine.HUDController.InventoryStats);
        }

        public void BeginBarter(Character character, SelectableObject interlocutor)
        {
            State = HumanControllerState.Bartering;
            if (Character.IsTalking)
                Engine.HUDController.SubtitlesFrame.Control.IsVisible = false;

            Engine.HUDController.BarterFrame.Control.Show();
            Engine.GuiManager.Pointer.Show();
            Engine.HUDController.FillBarterLists();
        }

        public void EndBarter(Character character, SelectableObject interlocutor)
        {
            if (Character.IsTalking == false)
            {
                State = HumanControllerState.Moving;
                Engine.GuiManager.Pointer.Hide();
            }
            else
            {
                State = HumanControllerState.Talking;
                Engine.HUDController.SubtitlesFrame.Control.IsVisible = true;
            }
                        
            Engine.HUDController.BarterFrame.Control.Hide();
            Engine.HUDController.ClearBarterLists();
            Engine.HUDController.ClearItemDescription(Engine.HUDController.BarterDescription, Engine.HUDController.BarterStats);
        }

        public void BeginPlunder(Character character, IContainer plundered)
        {
            State = HumanControllerState.Plundering;
            Engine.HUDController.InventoryFrame.Control.Show();
            Engine.HUDController.PlunderFrame.Control.Show();
            Engine.GuiManager.Pointer.Show();
            Engine.HUDController.FillPlunderList();
        }

        public void EndPlunder(Character character, IContainer plundered)
        {
            State = HumanControllerState.Moving;
            Engine.GuiManager.LeaveDragControl();
            Engine.GuiManager.Pointer.Hide();
            Engine.HUDController.InventoryFrame.Control.Hide();
            Engine.HUDController.PlunderFrame.Control.Hide();
            Engine.HUDController.ClearPlunderList();
            Engine.HUDController.ClearItemDescription(Engine.HUDController.InventoryDescription, Engine.HUDController.InventoryStats);      
        }      

        public void TalkChooseReplyBegin()
        {
            TalkState = HumanTalkState.ChoosingReply;
            Engine.GameCamera.LookAtCharacter();
            Engine.HUDController.RepliesFrame.Control.Show();
            Engine.HUDController.SubtitlesFrame.Control.Hide();
        }

        public void TalkPause()
        {
            TalkState = HumanTalkState.Pause;
            Engine.HUDController.RepliesFrame.Control.Hide();
            Engine.HUDController.SubtitlesFrame.Control.Hide();
            TalkDelayTime = 0.5f;
        }

        public void TalkListen()
        {
            TalkState = HumanTalkState.Listening;            
            Engine.HUDController.RepliesFrame.Control.Hide();
            Engine.HUDController.SubtitlesFrame.Control.Show();
            Engine.GameCamera.LookAtInterlocutor();
            TextIndex = 0;
            SetTalkText(CurrentNode.Text[0]);
        }

        public void TalkReply()
        {
            CurrentReply.CallActions();            
            TalkState = HumanTalkState.Replying;            
            Engine.HUDController.RepliesFrame.Control.Hide();
            Engine.HUDController.SubtitlesFrame.Control.Show();            
            TextIndex = 0;
            SetTalkText(CurrentReply.Text[0]);
        }

        public void SetTalkText(TalkText text)
        {
            Engine.HUDController.SubtitlesLabel.Caption = text.Text;
            UnloadCurrentVoice();
            text.LoadVoice();
            TalkDelayTime = text.Duration;            
            if (text.Voice != null)
            {
                CurrentVoice = text.Voice.PlayNewInstance();                
            }
        }

        public void TalkStartBarter()
        {
            ExchangeObject = Character.Interlocutor as IContainer;
            Character.BeginBarter(Character.Interlocutor);
        }
  
        public bool OnReplyClick(GuiControl sender)
        {
            CurrentReply = sender.UserData as TalkReply;
            TalkReply();
            return false;
        }        

        public void MovementAction()
        {            
            if (FocusObject != null && FocusObject.Exists)
            {
                if ((FocusObject.Position - Character.Position).Length < 1.5f)
                {
                    if (FocusObject is Described && (FocusObject as Described).IsPickable)
                    {                                              
                        Character.State.PickOrder(FocusObject as Described);
                    }
                    else if (FocusObject is Door)
                    {
                        Door door = FocusObject as Door;
                        if (door.IsOpen)
                            door.Close();                        
                        else                        
                            door.Open();                        
                    }
                    else if (FocusObject is Character)
                    {
                        if (!(FocusObject as Character).IsDead)
                        {
                            if (Character.IsNotBusy() && (FocusObject as Character).IsNotBusy() && FocusObject.TalkResponse != null)
                            {
                                CurrentNode = FocusObject.TalkResponse.PickReaction();
                                if (CurrentNode != null)
                                {
                                    Character.BeginConversation(FocusObject);
                                    FocusObject.BeginConversation(Character);
                                }
                            }
                            else
                                (FocusObject as Character).State.HideWeapon();
                        }
                        else
                        {
                            ExchangeObject = FocusObject as IContainer;
                            Character.BeginPlunder(ExchangeObject);
                        }
                    }
                    else if (!(FocusObject is Character) && FocusObject is IContainer && Character.IsNotBusy())
                    {
                        ExchangeObject = FocusObject as IContainer;
                        Character.BeginPlunder(ExchangeObject);
                    }                  
                }
                else
                    Character.State.MoveOrder(Run);
            }
        }

        void HandleArmedNothing()
        {
            if (Engine.IsKeyTyped(MOIS.KeyCode.KC_I))
                Character.State.EquipOrder();

            if (Engine.IsKeyTyped(MOIS.KeyCode.KC_1))
                Character.TakeM1H();
            if (Engine.IsKeyTyped(MOIS.KeyCode.KC_2))
                Character.TakePistol();  
            
            if (Engine.Keyboard.IsKeyDown(MOIS.KeyCode.KC_F) || Engine.MouseInput.ButtonPressed(MOIS.MouseButtonID.MB_Left))
                MovementAction();
            if (Engine.Keyboard.IsKeyDown(MOIS.KeyCode.KC_R) || Engine.MouseInput.ButtonPressed(MOIS.MouseButtonID.MB_Right))
            {
                if (FocusObject is Character && Character.IsNotBusy() && (FocusObject as Character).IsNotBusy())
                {
                    ExchangeObject = FocusObject as IContainer;
                    Character.BeginBarter(FocusObject);
                }
            }
        }

        void HandleUnarmed()
        {   
            if (Engine.IsKeyTyped(MOIS.KeyCode.KC_1))
                Character.ArmedCase = Character.ArmedCases.Nothing;           
        }

        void HandlePistol()
        {
            if (Engine.IsKeyTyped(MOIS.KeyCode.KC_2))
                Character.State.HideWeapon();

            if (Engine.MouseInput.ButtonPressed(MOIS.MouseButtonID.MB_Left))
            {                
                Character.Shoot();
            }
        }

        void HandleM1H()
        {
            if (Engine.IsKeyTyped(MOIS.KeyCode.KC_1))
                Character.State.HideWeapon();

            if (Engine.MouseInput.ButtonPressed(MOIS.MouseButtonID.MB_Left))
            {
                Character.M1HHit(FocusObject);
            }
        }

        void UpdateAimPoint()
        {
            Aim = Engine.MouseInput.ButtonDown(MOIS.MouseButtonID.MB_Right);            

            if (Aim && !Character.IsAiming)
                Character.State.AimOrder();
            else if (!Aim && Character.IsAiming)
                Character.State.StopAimingOrder();            
  

            Raycaster.PredicateRaycast apraycast = new Raycaster.PredicateRaycast(
                (body => body.UserData != Character 
                    && !(body.UserData is GameObject 
                        && (body.UserData as GameObject).IsObstacle == false))
                );            

            float rayDist = 100;
            apraycast.Go(Engine.NewtonWorld, Engine.GameCamera.Position, Engine.GameCamera.Position + Engine.GameCamera.Direction * rayDist);
            apraycast.SortContacts();
            if (apraycast.Contacts.Count > 0)
            {
                for (int contactNo = 0; contactNo < apraycast.Contacts.Count; contactNo++)
                {
                    Vector3 contactPt = Engine.GameCamera.Position + Engine.GameCamera.Direction * rayDist * apraycast.Contacts[contactNo].Distance;
                    if ((contactPt - Character.Position).NormalisedCopy.DotProduct(Character.Orientation * Vector3.UNIT_Z) > 0
                        && apraycast.Contacts[contactNo].Body.MaterialGroupID != Engine.MaterialManager.Materials["Transparent"])
                    {
                        Character.AimPoint = Engine.GameCamera.Position + Engine.GameCamera.Direction * rayDist * apraycast.Contacts[contactNo].Distance;
                        break;
                    }
                }
            }
            else
                Character.AimPoint = Engine.GameCamera.Position + Engine.GameCamera.Direction * rayDist;
        }

        void UpdateAutoAim()
        {
            if (Engine.Keyboard.IsKeyDown(MOIS.KeyCode.KC_F))
            {
                if (FocusObject != null && FocusObject.Exists)
                {
                    if (FocusObject is Character)
                        AimAt(FocusObject.Position + (FocusObject as Character).Profile.HeadOffset);
                    else
                        AimAt(FocusObject.Position);
                    LockTarget = true;
                }
                
            }
            else
                LockTarget = false;
        }

        public void AimAt(Vector3 targetPos)
        {
            Character.LookDirection = (targetPos - (Character.Position + Character.Orientation * Character.Profile.AimOffset)).NormalisedCopy;
        }

        void UpdateFocus()
        {
            float closestDist = 0;
            SelectableObject closestObject = null;
            bool ignoreDead = Character.ArmedCase != WastedMechanics.Character.ArmedCases.Nothing;

            foreach (GameObject go in Engine.ObjectManager.Objects)
            {
                if (go is SelectableObject && go != Character)
                {
                    if (ignoreDead && go is Character && (go as Character).IsDead)
                            continue;

                    float dotp = (go.Position - Engine.GameCamera.Position).NormalisedCopy.DotProduct(Engine.GameCamera.Direction);
                    float pcdotp = (go.Position - Character.Position).NormalisedCopy.DotProduct(Character.LookDirection);
                    
                    if (dotp > 0 && pcdotp > 0 && (closestObject == null || dotp > closestDist))
                    {
                        Raycaster.ObjectContactRaycast raycast = new Raycaster.ObjectContactRaycast(Character, ignoreDead);
                        raycast.Go(Engine.NewtonWorld, Engine.GameCamera.Position, go.Position);
                        if (raycast.Contact == go)
                        {
                            closestObject = go as SelectableObject;
                            closestDist = dotp;
                        }
                    }
                }
            }

            if (closestObject != null)
            {
                if (Character.ArmedCase == Character.ArmedCases.Nothing
                    || Character.ArmedCase == Character.ArmedCases.Unarmed)
                    if ((closestObject.Position - Character.Position).Length > 1.5f)
                        closestObject = null;
            }

            if (LockTarget == false)
            {
                Character.Contact = closestObject;
                FocusObject = closestObject;
            }

            if (FocusObject != null)
            {
                Engine.HUDController.FocusLabel.Caption = FocusObject.DisplayName;
                Vector2 screenPos = Helper.GetScreenCoordinates(FocusObject.Position + FocusObject.DisplayNameOffset);
                Engine.HUDController.FocusLabel.Width = Helper.GetTextWidth(Engine.HUDController.FocusLabel.TextArea);
                Engine.HUDController.FocusLabel.Height = 0.04f;
                Engine.HUDController.FocusLabel.Left = screenPos.x - Engine.HUDController.FocusLabel.Width * 0.5f;
                Engine.HUDController.FocusLabel.Top = screenPos.y;
                Engine.HUDController.FocusLabel.IsVisible = FocusObject.ShowName;
            }
            else
                Engine.HUDController.FocusLabel.IsVisible = false;
        }
       
        public void HandleMovement()
        {
            UpdateAimPoint();
            UpdateAutoAim();
            UpdateFocus();

            switch (Character.ArmedCase)
            {
                case Character.ArmedCases.Nothing:
                    HandleArmedNothing();
                    break;
                case Character.ArmedCases.Unarmed:
                    HandleUnarmed();
                    break;
                case Character.ArmedCases.Pistol:
                    HandlePistol();
                    break;
                case Character.ArmedCases.M1H:
                    HandleM1H();
                    break;
            }

            if (Engine.IsKeyTyped(MOIS.KeyCode.KC_R))                            
                Run = !Run;

            if (Engine.IsKeyTyped(MOIS.KeyCode.KC_C))
                Character.StartClimb();
            if (Engine.IsKeyTyped(MOIS.KeyCode.KC_SPACE))
                Character.State.JumpOrder();

            Degree rotationDeltay = (float)System.Math.Pow(System.Math.Abs(Engine.MouseInput.Delta.y) * 50, 1.5)  * System.Math.Sign(Engine.MouseInput.Delta.y);
            Degree rotationDeltax = -(float)System.Math.Pow(System.Math.Abs(Engine.MouseInput.Delta.x) * 90, 1.5) * System.Math.Sign(Engine.MouseInput.Delta.x);                        
            if (Engine.Keyboard.IsKeyDown(MOIS.KeyCode.KC_Q))
                rotationDeltax = 1.8f;
            if (Engine.Keyboard.IsKeyDown(MOIS.KeyCode.KC_E))
                rotationDeltax = -1.8f;

            Character.VerticalAngle += rotationDeltay;
            
            Engine.GameCamera.Distance += 0.002f * Engine.MouseInput.ScrollAmount;

            if (Engine.Keyboard.IsModifierDown(MOIS.Keyboard.Modifier.Alt) == false)
            {
                if (Character.CanTurn)
                {
                    if (rotationDeltax != 0)
                        Character.HorizontalAngle += rotationDeltax;                        
                }
                if (Engine.Keyboard.IsKeyDown(MOIS.KeyCode.KC_W))
                    Character.State.MoveOrder(Run);
                else if (Character.IsMoving)
                    Character.State.HaltOrder();                    
            }            
        }



        public void HandleConversation()
        {
            TalkDelayTime -= Engine.FixedTimeStep;

            if (Engine.IsKeyTyped(MOIS.KeyCode.KC_ESCAPE))
                TalkDelayTime = 0;

            if (TalkState == HumanTalkState.Listening)
            {                
                if (TalkDelayTime <= 0)
                {
                    TextIndex++;
                    if (TextIndex == CurrentNode.Text.Count)
                    {
                        if (CurrentNode.IsEnding)
                        {
                            Character.Interlocutor.EndConversation();
                            Character.EndConversation();
                        }
                        else
                        {
                            Engine.GameCamera.LookAtCharacter();
                            Engine.HUDController.FillRepliesList(CurrentNode);
                            if (Engine.HUDController.RepliesList.Items.Count == 1)                                                            
                                OnReplyClick(Engine.HUDController.RepliesList.Items[0]);                            
                            else
                                TalkChooseReplyBegin();
                        }
                    }
                    else
                    {
                        Engine.GameCamera.LookAtInterlocutor();
                        SetTalkText(CurrentNode.Text[TextIndex]);
                    }
                }
            }
            else if (TalkState == HumanTalkState.Replying)
            {                                
                if (TalkDelayTime <= 0)
                {
                    TextIndex++;
                    if (TextIndex == CurrentReply.Text.Count)
                    {
                        if (CurrentReply.IsEnding)
                        {
                            Character.Interlocutor.EndConversation();
                            Character.EndConversation();
                        }
                        else
                        {
                            CurrentNode = CurrentReply.Response.PickReaction();
                            TalkPause();
                        }
                    }
                    else
                    {
                        Engine.GameCamera.LookAtCharacter();
                        SetTalkText(CurrentReply.Text[TextIndex]);
                    }
                }
            }
            else if (TalkState == HumanTalkState.Pause)
            {
                if (TalkDelayTime <= 0)
                    TalkListen();
            }
        }

        void HandlePlundering()
        {
            if (Engine.IsKeyTyped(MOIS.KeyCode.KC_ESCAPE) || Engine.IsKeyTyped(MOIS.KeyCode.KC_I))
                Character.EndPlunder();
        }

        void HandleEquipping()
        {
            if (Engine.IsKeyTyped(MOIS.KeyCode.KC_ESCAPE) || Engine.IsKeyTyped(MOIS.KeyCode.KC_I))
                Character.State.EquipFinishOrder();
        }

        void HandleBartering()
        {
            if (Engine.IsKeyTyped(MOIS.KeyCode.KC_ESCAPE) || Engine.IsKeyTyped(MOIS.KeyCode.KC_I))
                Character.EndBarter();
        }
     
        public void Update()
        {
            if (Character != null)
            {
                Engine.GameCamera.Update();
                                
                if (State == HumanControllerState.Moving)
                    HandleMovement();
                else if (State == HumanControllerState.Talking)
                    HandleConversation();
                else if (State == HumanControllerState.Plundering)
                    HandlePlundering();
                else if (State == HumanControllerState.Equipping)
                    HandleEquipping();
                else if (State == HumanControllerState.Bartering)
                    HandleBartering();
                else if (State == HumanControllerState.GameEnd)
                {
                    Engine.HUDController.GameEndFrame.Control.IsVisible = true;
                }

                if (Engine.IsKeyTyped(MOIS.KeyCode.KC_O))
                {
                    if (FocusObject != null && FocusObject is Character)
                        AttachCharacter(FocusObject as Character);
                }

            }
        }
    }
}
